package aor2.assembly;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;
import aor2.components.InstructionCode;
import aor2.memory.Memory;

//TODO FALE GRESKE!
public class Assembler {
	public static ArrayList<Long> instruction(String line) {
		String tmp[] = line.split(" ");
		/*
		 * for(int i = 0; i<tmp.length; i++){ System.out.println(tmp[i]); }
		 */
		long instruction = 0x0000L;
		long instruction1 = Long.MIN_VALUE;
		long instruction2 = Long.MIN_VALUE;
		int type = -1;
		if (tmp[0].toUpperCase().equals("MOV")) {
			instruction = InstructionCode.MOV;
			type = 2;
		}
		if (tmp[0].toUpperCase().equals("OR")) {
			instruction = InstructionCode.OR;
			type = 2;
		}

		if (tmp[0].toUpperCase().equals("SUB")) {
			instruction = InstructionCode.SUB;
			type = 2;
		}

		if (tmp[0].toUpperCase().equals("INC")) {
			instruction = InstructionCode.INC;
			type = 1;
		}

		if (tmp[0].toUpperCase().equals("DEC")) {
			instruction = InstructionCode.DEC;
			type = 1;
		}

		if (tmp[0].toUpperCase().equals("NOT")) {
			instruction = InstructionCode.NOT;
			type = 1;
		}

		if (tmp[0].toUpperCase().equals("ASR")) {
			instruction = InstructionCode.ASR;
			type = 1;
		}

		if (tmp[0].toUpperCase().equals("JMPIND")) {
			instruction = InstructionCode.JMPIND;
			type = 1;
		}

		if (tmp[0].toUpperCase().equals("JSRIND")) {
			instruction = InstructionCode.JSRIND;
			type = 1;
		}

		if (tmp[0].toUpperCase().equals("RTS")) {
			instruction = InstructionCode.RTS;
			type = 0;
		}

		if (tmp[0].toUpperCase().equals("RTI")) {
			instruction = InstructionCode.RTI;
			type = 0;
		}

		if (tmp[0].toUpperCase().equals("INTE")) {
			instruction = InstructionCode.INTE;
			type = 0;
		}

		if (tmp[0].toUpperCase().equals("INTD")) {
			instruction = InstructionCode.INTD;
			type = 0;
		}

		if (tmp[0].toUpperCase().equals("TRPE")) {
			instruction = InstructionCode.TRPE;
			type = 0;
		}

		if (tmp[0].toUpperCase().equals("TRPD")) {
			instruction = InstructionCode.TRPD;
			type = 0;
		}

		if (tmp[0].toUpperCase().equals("BNZ")) {
			instruction = InstructionCode.BNZ;
			type = 3;
		}

		if (tmp[0].toUpperCase().equals("BSR")) {
			instruction = InstructionCode.BSR;
			type = 3;
		}

		if (tmp[0].toUpperCase().equals("BRN")) {
			instruction = InstructionCode.BRN;
			type = 3;
		}

		if (tmp[0].toUpperCase().equals("INT")) {
			instruction = InstructionCode.INT;
			type = 3;
		}

		if (type == 2) {
			if (tmp[1].length() == 2) {
				// registarsko diretno
				int n = Character.getNumericValue(tmp[1].charAt(1));
				System.out.println(Long.toHexString(n));
				n = n << 6;
				instruction = instruction | n;

			} else if (tmp[1].length() >= 4) {
				char c1 = tmp[1].charAt(0);
				if (c1 != '[') {
					int n = Character.getNumericValue(tmp[1].charAt(1));
					n = n << 6;

					if (tmp[1].charAt(2) == '+') {
						// postinc
						if (tmp[1].charAt(3) == '+') {
							long adr = InstructionCode.postinc1;
							instruction = instruction | adr | n;

						} else {
							long adr = InstructionCode.index1;
							instruction = instruction | adr | n;
							String br = "";
							br = tmp[1].substring(3);

							int pom1 = Integer.parseInt(br);
							if (pom1 <= 65535)
								instruction1 = pom1;
							else
								type = -1; // greska pri pomeraju!
							System.out.println(pom1);
						}
					} else if (tmp[1].charAt(2) == '-') {
						if (tmp[1].charAt(3) == '-') {
							// postinc
							long adr = InstructionCode.predec1;
							instruction = instruction | adr | n;

						} else {
							long adr = InstructionCode.index1;
							instruction = instruction | adr | n;
							String br = "";
							br = tmp[1].substring(2);
							int pom1 = Integer.parseInt(br);
							if ((pom1 <= 65535) && (pom1 > -65356))
								instruction1 = pom1;
							else
								type = -1; // greska pri pomeraju!

						}
					}
				} else {
					int n = Character.getNumericValue(tmp[1].charAt(2));
					n = n << 6;
					if (tmp[1].charAt(3) == ']') {
						long adr = InstructionCode.regind1;
						instruction = instruction | n | adr;
					} else if (tmp[1].charAt(3) == '+') {
						if (tmp[1].charAt(4) == '+') {
							long adr = InstructionCode.postincind1;
							instruction = instruction | adr | n;

						} else {
							long adr = InstructionCode.indexind1;
							instruction = instruction | adr | n;
							String br = "";
							int j = tmp[1].indexOf(']');
							br = tmp[1].substring(4, j);

							int pom1 = Integer.parseInt(br);
							if (pom1 <= 65535)
								instruction1 = pom1;
							else
								type = -1; // greska pri pomeraju!

						}
					} else if (tmp[1].charAt(3) == '-') {
						if (tmp[1].charAt(4) == '-') {
							long adr = InstructionCode.predecind1;
							instruction = instruction | adr | n;

						} else {
							long adr = InstructionCode.indexind1;
							instruction = instruction | adr | n;
							String br = "";
							int j = tmp[1].indexOf(']');
							br = tmp[1].substring(3, j);

							int pom1 = Integer.parseInt(br);
							if (pom1 <= 65535)
								instruction1 = pom1;
							else
								type = -1; // greska pri pomeraju!

						}
					}
				}

			}

			if (tmp[2].length() == 2) {
				// registarsko diretno
				int n = Character.getNumericValue(tmp[2].charAt(1));
				instruction = instruction | n;

			} else if (tmp[2].length() >= 4) {
				char c1 = tmp[2].charAt(0);
				if (c1 != '[') {
					int n = Character.getNumericValue(tmp[2].charAt(1));
					if (tmp[2].charAt(2) == '+') {
						// postinc
						if (tmp[2].charAt(3) == '+') {
							long adr = InstructionCode.postinc2;
							instruction = instruction | adr | n;

						} else {
							long adr = InstructionCode.index2;
							instruction = instruction | adr | n;
							String br2 = "";
							br2 = tmp[2].substring(3);

							int pom2 = Integer.parseInt(br2);
							if (pom2 <= 65535)
								instruction2 = pom2;
							else
								type = -1; // greska pri pomeraju!

						}
					} else if (tmp[2].charAt(2) == '-') {
						if (tmp[2].charAt(3) == '-') {
							// postinc
							long adr = InstructionCode.predec2;
							instruction = instruction | adr | n;

						} else {
							long adr = InstructionCode.index2;
							instruction = instruction | adr | n;
							String br2 = "";
							br2 = tmp[2].substring(2);

							int pom2 = Integer.parseInt(br2);
							if (pom2 <= 65535)
								instruction2 = pom2;
							else
								type = -1; // greska pri pomeraju!

						}
					}
				} else {
					int n = Character.getNumericValue(tmp[2].charAt(2));
					if (tmp[2].charAt(3) == ']') {
						long adr = InstructionCode.regind2;
						instruction = instruction | n | adr;
					} else if (tmp[2].charAt(3) == '+') {
						if (tmp[2].charAt(4) == '+') {
							long adr = InstructionCode.postincind2;
							instruction = instruction | adr | n;

						} else {
							long adr = InstructionCode.indexind2;
							instruction = instruction | adr | n;
							String br2 = "";
							int l = tmp[2].indexOf(']');
							br2 = tmp[2].substring(4, l);

							int pom2 = Integer.parseInt(br2);
							if (pom2 <= 65535)
								instruction2 = pom2;
							else
								type = -1; // greska pri pomeraju!
							;
						}
					} else if (tmp[2].charAt(3) == '-') {
						if (tmp[2].charAt(4) == '-') {
							long adr = InstructionCode.predecind2;
							instruction = instruction | adr | n;

						} else {
							long adr = InstructionCode.indexind2;
							instruction = instruction | adr | n;
							String br2 = "";
							int l = tmp[2].indexOf(']');
							br2 = tmp[2].substring(3, l);

							int pom2 = Integer.parseInt(br2);
							if (pom2 <= 65535)
								instruction2 = pom2;
							else
								type = -1; // greska pri pomeraju!

						}
					}
				}

			}

		}

		if (type == 1) {

			if (tmp[1].length() == 2) {
				// registarsko diretno
				int n = Character.getNumericValue(tmp[1].charAt(1));
				instruction = instruction | n;

			} else if (tmp[1].length() >= 4) {
				char c1 = tmp[1].charAt(0);
				if (c1 != '[') {
					int n = Character.getNumericValue(tmp[1].charAt(1));
					if (tmp[1].charAt(2) == '+') {
						// postinc
						if (tmp[1].charAt(3) == '+') {
							long adr = InstructionCode.postinc2;
							instruction = instruction | adr | n;

						} else {
							long adr = InstructionCode.index2;
							instruction = instruction | adr | n;
							String br2 = "";
							br2 = tmp[1].substring(3);

							int pom2 = Integer.parseInt(br2);
							if (pom2 <= 65535)
								instruction2 = pom2;
							else
								type = -1; // greska pri pomeraju!

						}
					} else if (tmp[1].charAt(2) == '-') {
						if (tmp[1].charAt(3) == '-') {
							// postinc //FIXME predec valjda?
							long adr = InstructionCode.predec2;
							instruction = instruction | adr | n;

						} else {
							long adr = InstructionCode.index2;
							instruction = instruction | adr | n;
							String br2 = "";
							br2 = tmp[1].substring(2);

							int pom2 = Integer.parseInt(br2);
							if (pom2 <= 65535)
								instruction2 = pom2;
							else
								type = -1; // greska pri pomeraju!

						}
					}
				} else {
					int n = Character.getNumericValue(tmp[1].charAt(2));
					if (tmp[1].charAt(3) == ']') {
						long adr = InstructionCode.regind2;
						instruction = instruction | n | adr;
					} else if (tmp[1].charAt(3) == '+') {
						if (tmp[1].charAt(4) == '+') {
							long adr = InstructionCode.postincind2;
							instruction = instruction | adr | n;

						} else {
							long adr = InstructionCode.indexind2;
							instruction = instruction | adr | n;
							String br2 = "";
							int l = tmp[1].indexOf(']');
							br2 = tmp[1].substring(4, l);

							int pom2 = Integer.parseInt(br2);
							if (pom2 <= 65535)
								instruction2 = pom2;
							else
								type = -1; // greska pri pomeraju!

						}
					} else if (tmp[1].charAt(3) == '-') {
						if (tmp[1].charAt(4) == '-') {
							long adr = InstructionCode.predecind2;
							instruction = instruction | adr | n;

						} else {
							long adr = InstructionCode.indexind2;
							instruction = instruction | adr | n;
							String br2 = "";
							int l = tmp[1].indexOf(']');
							br2 = tmp[1].substring(3, l);

							int pom2 = Integer.parseInt(br2);
							if (pom2 <= 65535)
								instruction2 = pom2;
							else
								type = -1; // greska pri pomeraju!

						}
					}
				}

			}
		}

		if (type == 0) {
			// ostaje normalno
		}

		if (type == 3) {
			if (tmp[1].charAt(0) != '-') {
				int n = Integer.parseInt(tmp[1]);
				if (n <= 127)
					instruction = instruction | n;
				else
					System.out.println("GRESKA U POMERAJU");
			}

			else {
				int n = Integer.parseInt(tmp[1]);
				int bitn = n & 0xFF;
				if (n >= (-128))
					instruction = instruction | bitn;
				else
					System.out.println("GRESKA U POMERAJU");

			}
		}
		// System.out.println(Long.toHexString(instruction));
		// System.out.println(Long.toHexString(instruction1));
		// System.out.println(Long.toHexString(instruction2));
		ArrayList<Long> list = new ArrayList<Long>();
		if (type != -1) {
			list.add(instruction);
			if (instruction1 > Long.MIN_VALUE)
				list.add(instruction1);
			if (instruction2 > Long.MIN_VALUE)
				list.add(instruction2);

//			System.out.println(list.toString());
			return list;
		} else
			System.out.println("GRESKA!");
		return null;
	}

	public static void load(String asmFile, Memory mem, int startMemAdr) {
		int adr = startMemAdr;
		try {
			Scanner s = new Scanner(new File(asmFile));
			while (s.hasNext()) {
				String line = s.nextLine();
				ArrayList<Long> instr = instruction(line);
				java.util.Iterator<Long> inter = instr.iterator();
				while (inter.hasNext()) {
					mem.writeWord(adr++, inter.next());
				}

			}
			s.close();
		} catch (FileNotFoundException e) {
			System.err.append("uhvacen:");
			e.printStackTrace();
		}

	}

}
